#!/usr/bin/python2.4


import os
import shutil
import sys
import threading
import time
import traceback

import constants


class Logger(object):

  LEVELS = ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'FATAL')

  class __metaclass__(type):

    def __init__(self, name, bases, class_dict):
      for i, level in enumerate(class_dict['LEVELS']):
        setattr(self, level, i)

  def __init__(self, filename=None, level=0, max_log_size_in_mb=128):
    if filename:
      self.setup(filename, level, max_log_size_in_mb)

  def __repr__(self):
    return '%s(filename=%r @ %s)' % (type(self).__name__,
                                     self.base_filename,
                                     self.started_time)

  def __getattr__(self, name):

    def logging_wrapper(message='', caller_level=3):
      return self.logging(i, message, caller_level)

    if name.islower():
      name = name.upper()
      for i, level in enumerate(self.LEVELS):
        if name == level:
          return logging_wrapper

    raise AttributeError('%r object has no attribute %r' %
                         (type(self).__name__, name))

  def setup(self, filename, level=0, max_log_size_in_mb=128):
    self.lock = threading.RLock()
    self.started_time = time.strftime('%Y%m%d.%H%M%S')
    self.base_filename = filename
    self.filenames = []
    for level_name in self.LEVELS:
      self.filenames.append(os.path.join(constants.LOGGING_DIR,
                                         '%s.%s.%s' % (filename,
                                                       level_name,
                                                       self.started_time)))
    self.files = [None] * len(self.LEVELS)
    self.set_level(level)
    self.max_log_size_in_mb = max_log_size_in_mb

  def set_level(self, level):
    self.level = level

  def print_to_file(self, f, prefix, message):
    message = message.rstrip()
    if '\n' in message:
      print >>f, prefix, ' MULTIPLE LINES LOGGING BEGIN '.center(80, '-')
      print >>f, message
      print >>f, prefix, ' MULTIPLE LINES LOGGING END '.center(80, '-')
    else:
      print >>f, prefix, message
    f.flush()

  def logging(self, level, message='', caller_level=2):
    try:
      self.lock.acquire()
      filename, lineno, _, _ = traceback.extract_stack(limit=caller_level)[0]
      prefix = '%s %s %s:%s]' % (self.LEVELS[level][0],
                                time.strftime('%Y-%m-%d %H:%M:%S'),
                                os.path.basename(filename),
                                lineno)
      for l in xrange(level, -1, -1):
        if self.files[l] is None:
          self.files[l] = file(self.filenames[l], 'w')
          sym_link = self.filenames[l][:-16]
          if os.path.lexists(sym_link):
            os.unlink(sym_link)
          os.symlink(self.filenames[l], sym_link)
        self.print_to_file(self.files[l], prefix, message)
        if self.files[l].tell() >= self.max_log_size_in_mb * 1024 * 1024:
          self.files[l].close()
          shutil.move(self.filenames[l], '%s.last' % self.filenames[l])
          self.files[l] = file(self.filenames[l], 'w')

      if level >= self.level:
        self.print_to_file(sys.stdout, prefix, message)
      if level >= self.FATAL:
        os.abort()

    finally:
      self.lock.release()


logger = dioxide_logger = Logger('dioxide', Logger.WARNING)
rpc_logger = Logger('dioxide.rpc', Logger.ERROR)
